package com.gmaf.mas.launch.property;

import java.text.ParsePosition;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class DefaultPropertyParser implements PropertyParser
{
	private static final Log log = LogFactory.getLog(DefaultPropertyParser.class);
	
	public DefaultPropertyParser()
	{
	}
	/**
	 * Decode properties from a String representation.
	 * 
	 * <ul>
	 * 
	 * <li>This implementation starts parsing the <code>value</code> parameter
	 * (unsurprisingly) at the beginning and asks each {@link PropertyParser
	 * PropertyExpander} whether there is a property reference at that point.
	 * PropertyExpanders return the name of a property they may find and may
	 * advance the parse position.</li>
	 * 
	 * <li>If the PropertyExpander returns <code>null</code> the method
	 * continues with the next PropertyExpander, otherwise it tries to look up
	 * the property's value using the configured {@link GetProperty GetProperty}
	 * instance.</li>
	 * 
	 * <li>Once all PropertyExpanders have been consulted, the parse position is
	 * advanced by one character and the process repeated until
	 * <code>value</code> is exhausted.</li>
	 * 
	 * </ul>
	 * 
	 * <p>
	 * If the entire contents of <code>value</code> resolves to a single
	 * property, the looked up property value is returned. Otherwise a String is
	 * returned that concatenates the non-property parts of <code>value</code>
	 * and the expanded values of the properties that have been found.
	 * </p>
	 * 
	 * @param value
	 *            The string to be scanned for property references. May be
	 *            <code>null</code>, in which case this method returns
	 *            immediately with no effect.
	 * 
	 * @return the original string with the properties replaced, or
	 *         <code>null</code> if the original string is <code>null</code>.
	 */
	public String parseProperty(String value, Configuration config)
	{
		if(value == null || "".equals(value))
		{
			return value;
		}
		final int len = value.length();
		ParsePosition pos = new ParsePosition(0);
		String o = parsePropertyValue(value, pos, config);
		if(o != null && pos.getIndex() >= len)
		{
			return o;
		}
		StringBuffer sb = new StringBuffer(len * 2);
		if(o == null)
		{
			sb.append(value.charAt(pos.getIndex()));
			pos.setIndex(pos.getIndex() + 1);
		}
		else
		{
			sb.append(o);
		}
		while(pos.getIndex() < len)
		{
			o = parsePropertyValue(value, pos, config);
			if(o == null)
			{
				sb.append(value.charAt(pos.getIndex()));
				pos.setIndex(pos.getIndex() + 1);
			}
			else
			{
				sb.append(o);
			}
		}
		return sb.toString();
	}

	@Override
	public String parsePropertyValue(String value, ParsePosition pos, Configuration config)
	{
		final int start = pos.getIndex();

		if(start > value.length())
		{
			// early exit, can't find any property here, no need to
			// consult all the delegates.
			return null;
		}

		String propertyName = parsePropertyName(value, pos, config);
		if(propertyName != null)
		{
			String result = config.getProperty(propertyName);
			if(result != null)
			{
				return result;
			}
			
			if(log.isDebugEnabled())
			{
				log.debug("Property \"" + propertyName + "\" has not been set");
			}
			return value.substring(start, pos.getIndex());
		}
		return null;
	}

	public String parsePropertyName(String s, ParsePosition pos, Configuration config)
	{
		int index = pos.getIndex();
		// directly check near, triggering characters:
		if(s.length() - index >= 3 && '$' == s.charAt(index) && '{' == s.charAt(index + 1))
		{
			int start = index + 2;
			// defer to String.indexOf() for protracted check:
			int end = s.indexOf('}', start);
			if(end < 0)
			{
				throw new IllegalArgumentException("Syntax error in property: "
					+ s.substring(index));
			}
			pos.setIndex(end + 1);
			return start == end ? "" : s.substring(start, end);
		}
		return null;
	}

//	private String getProperty(String propertyName)
//	{
//		return root.getProperty(propertyName);
//	}
}
